package com.leyantech.ecp.agenx.common.service;

import com.leyantech.ecp.agenx.common.entity.HelaKey;
import com.leyantech.ecp.agenx.common.utils.JsonUtils;
import com.leyantech.ecp.agenx.common.utils.RetryUtils;
import com.leyantech.hela.client.core.v2.HelaClientService;
import com.leyantech.hela.v2.constant.ResponseCodeEnum;
import com.leyantech.hela.v2.dto.BatchGetStoreConfigDto;
import com.leyantech.hela.v2.dto.BatchGetStoreConfigRspDto;
import com.leyantech.hela.v2.dto.GetStoreConfigDto;
import com.leyantech.hela.v2.dto.GetStoreConfigRspDto;
import com.leyantech.utility.Logger;
import com.leyantech.utility.constructs.Constructs;

import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author jie.chen, {@literal <jie.chen@leyantech.com>}
 * @date 2020-09-05.
 */
@Singleton
public class HelaService {

  private static final Logger LOGGER = new Logger(HelaService.class);

  private final HelaClientService helaClientService;

  /**
   * 初始化.
   *
   * @param helaClientService helaClientService.
   */
  @Inject
  public HelaService(HelaClientService helaClientService) {
    this.helaClientService = helaClientService;
  }

  /**
   * 从hela获取实体.
   *
   * @param sid     店铺id.
   * @param helaKey key.
   * @param <T>     泛型.
   * @return entity.
   */
  public <T> Optional<T> get(String sid, HelaKey helaKey) {
    GetStoreConfigDto dto = GetStoreConfigDto.builder().sid(sid)
        .key(helaKey.getKey())
        .operator(Constructs.getAppNameOrDefault())
        .build();
    try {
      GetStoreConfigRspDto getStoreConfigRspDto = RetryUtils
          .retryCall(() -> helaClientService.get(dto));
      if (getStoreConfigRspDto.getCode() != ResponseCodeEnum.SUCCESS) {
        LOGGER.error("call hela fail!dto:{},code:{},msg:{}",
            dto, getStoreConfigRspDto.getCode(), getStoreConfigRspDto.getMsg());
        return Optional.empty();
      }
      return Optional.ofNullable(getStoreConfigRspDto.getValue())
          .filter(StringUtils::isNotBlank)
          .map(v -> JsonUtils.fromJson(v, helaKey.getType()));
    } catch (Exception e) {
      LOGGER.error(e, "call hela meet exception, dto: {} ", dto);
      return Optional.empty();
    }
  }

  public Map<HelaKey, Object> batchGet(String storeId, List<HelaKey> keys) {
    final BatchGetStoreConfigDto request = BatchGetStoreConfigDto.builder()
        .sid(storeId)
        .keys(keys.stream().map(HelaKey::getKey).collect(Collectors.toList()))
        .operator(Constructs.getAppNameOrDefault())
        .build();
    try {
      BatchGetStoreConfigRspDto batchGetStoreConfigRspDto =
          RetryUtils.retryCall(() -> helaClientService.batchGet(request));
      ResponseCodeEnum responseCodeEnum = batchGetStoreConfigRspDto.getCode();
      if (responseCodeEnum != ResponseCodeEnum.SUCCESS) {
        if (responseCodeEnum == ResponseCodeEnum.CLIENT_NOT_FOUND) {
          //如果response code为CLIENT NOT FOUND，表明hela里这个key没有存储过value
          return Collections.emptyMap();
        } else {
          LOGGER.error("call hela fail!dto:{},code:{},msg:{}", request, responseCodeEnum,
              batchGetStoreConfigRspDto.getMsg());
        }
      }
      final Map<String, String> kvs = batchGetStoreConfigRspDto.getKvs();

      return keys.parallelStream()
          .map(helaKey -> {
            final String json = kvs.get(helaKey.getKey());
            try {
              if (StringUtils.isNotBlank(json) && !"null".equalsIgnoreCase(json)) {
                return Pair.of(helaKey, JsonUtils.fromJson(json, helaKey.getType()));
              }
            } catch (Exception e) {
              LOGGER.error("{} {} parse json failed", helaKey.getKey(), json, e);
            }
            return null;
          })
          .filter(Objects::nonNull)
          .collect(Collectors.toMap(Pair::getKey, Pair::getValue));
    } catch (Exception e) {
      LOGGER.error(e, "call hela meet exception, dto: {} ", request);
    }
    return Collections.emptyMap();
  }

  public interface HelaGetter {

    <T> T get(HelaKey helaKey);
  }

  public HelaGetter batchGetter(String storeId, List<HelaKey> keys) {

    final Map<HelaKey, Object> config = this.batchGet(storeId, keys);

    return new HelaGetter() {

      @Override
      @SuppressWarnings("unchecked")
      public <T> T get(HelaKey helaKey) {
        return (T) config.get(helaKey);
      }
    };

  }


}
